package com.qd.system.service.person;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.db.BaseService;
import com.cdqidi.dto.CacheKey;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.RedisTemplateUtil;
import com.qd.common.sys.constant.Constants;
import com.qd.common.sys.domain.entity.person.PersonRoleDTO;
import com.qd.common.sys.domain.entity.role.RoleDTO;
import com.qd.common.sys.domain.entity.role.RolePersonRelationsDTO;
import com.qd.system.mapper.PersonRoleMapper;
import com.qd.system.model.PersonRole;
import com.qd.system.model.Role;
import com.qd.system.service.role.RoleService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;

import static java.util.stream.Collectors.toList;

/**
 * @author sjk
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class PersonRoleService extends BaseService<PersonRoleMapper, PersonRole> {

    private static final String PREFIX_ID = "sys_person_role_personRoleId_{0}";
    private static final String PREFIX_UNIQUE_PERSON_ID = "sys_person_role_personId_{0}_roleId_{1}";
    private static final String PREFIX_PERSON_ID = "sys_person_role_personId_{0}";

    private final RedisTemplateUtil<PersonRole> redisTemplateUtil;
    private final RedisTemplateUtil<RoleDTO> roleRedisTemplateUtil;
    private final PersonService personService;
    private final RoleService roleService;

    /**
     * 保存
     *
     * @param dto 页面对象
     * @return 页面对象
     */
    @Transactional(rollbackFor = Exception.class)
    public PersonRoleDTO save(PersonRoleDTO dto) {
        final PersonRole model = dtoToModel(dto);
        try {
            if (super.save(model)) {
                return modelToDto(model);
            }
            throw new ApiException("添加失败");
        } finally {
            clearCache(model);
            model.freeData();
        }
    }

    /**
     * 修改
     *
     * @param dto 页面对象
     * @return 页面对象
     */
    @Transactional(rollbackFor = Exception.class)
    public PersonRoleDTO update(PersonRoleDTO dto) {
        final PersonRole historyModel = Optional.ofNullable(getById(dto.getPersonRoleId())).orElseThrow(() -> new ApiException("ID不存在,不能更新"));
        final PersonRole model = dtoToModel(dto);
        try {
            if (super.updateById(model)) {
                return modelToDto(model);
            }
            throw new ApiException("更新失败");
        } finally {
            clearCache(model);
            clearCache(historyModel);
            historyModel.freeData();
            model.freeData();
        }
    }

    /**
     * 批量添加
     *
     * @param mList 数据库对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveBatch(List<PersonRole> mList) throws ApiException {
        try {
            if (!super.saveBatch(mList)) {
                throw new ApiException("批量添加失败");
            }
        } finally {
            clearCaches(mList);
        }
    }

    /**
     * 删除，单个
     *
     * @param personRoleId 主键
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeById(String personRoleId) {
        final PersonRole model = Optional.ofNullable(getById(personRoleId)).orElseThrow(() -> new ApiException("数据不存在,不能删除"));
        try {
            if (!super.removeById(personRoleId)) {
                throw new ApiException("删除失败");
            }
        } finally {
            clearCache(model);
            model.freeData();
        }
    }

    /**
     * 批量删除
     *
     * @param ids 主键集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeBatchIds(List<String> ids) {
        List<PersonRole> historyModelList = checkIds(ids);
        try {
            if (!super.removeByIds(ids)) {
                throw new ApiException("批量删除失败");
            }
        } finally {
            clearCaches(historyModelList);
            historyModelList.clear();
        }
    }

    /**
     * 根据传入对象构建查询参数，判断是否存在
     *
     * @param dto 参数
     * @return 存在为true
     */
    @Transactional(readOnly = true)
    public Boolean isExists(PersonRoleDTO dto) {
        final QueryWrapper<PersonRole> queryWrapper = createQueryWrapper(dto);
        final boolean exists = super.count(queryWrapper) > 0;
        queryWrapper.clear();
        return exists;
    }

    /**
     * 根据传入对象构建查询参数，判断是否存在
     *
     * @param model 参数
     * @return 存在为true
     */
    @Transactional(readOnly = true)
    public Boolean isExists(PersonRole model) {
        final PersonRoleDTO dto = modelToDto(model);
        return isExists(dto);
    }

    /**
     * 根据主键判断数据是否存在
     *
     * @param personRoleId 主键
     * @return 存在为true
     */
    @Transactional(readOnly = true)
    public Boolean isExistsById(String personRoleId) {
        boolean exists = false;
        if (StringUtils.hasLength(personRoleId)) {
            final PersonRole model = getById(personRoleId);
            exists = (null != model);
            if (exists) {
                model.freeData();
            }
        }
        return exists;
    }

    /**
     * 根据主键获取数据
     *
     * @param personRoleId 主键
     * @return 单条记录
     */
    @Transactional(readOnly = true)
    public PersonRole getById(String personRoleId) {
        final String key = RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, personRoleId);
        return redisTemplateUtil.get(new CacheKey().setKey(key).setTimeout(ConstantDto.REDIS_TIME_OUT_DAY), () -> super.getById(personRoleId), PersonRole.class);
    }

    /**
     * 根据主键获取数据
     *
     * @param personRoleId 主键
     * @return 单条记录，转换成页面对象
     */
    @Transactional(readOnly = true)
    public PersonRoleDTO getByIdDto(String personRoleId) {
        final PersonRole model = getById(personRoleId);
        if (null != model) {
            return modelToDto(model);
        }
        return null;
    }

    /**
     * 根据条件查询单条数据
     *
     * @param dto 查询参数
     * @return 单条记录
     */
    @Transactional(readOnly = true)
    public PersonRole getOne(PersonRoleDTO dto) {
        final QueryWrapper<PersonRole> queryWrapper = createQueryWrapper(dto);
        final PersonRole model = super.getOne(queryWrapper);
        queryWrapper.clear();
        return model;
    }

    /**
     * 根据条件查询单条数据
     *
     * @param dto 查询参数
     * @return 单条记录，转换成页面对象
     */
    @Transactional(readOnly = true)
    public PersonRoleDTO getOneDto(PersonRoleDTO dto) {
        final PersonRole model = getOne(dto);
        if (null == model) {
            return null;
        }
        final PersonRoleDTO newDto = modelToDto(model);
        model.freeData();
        return newDto;
    }

    /**
     * 根据条件查询列表数据
     *
     * @param dto 查询参数
     * @return 列表数据
     */
    @Transactional(readOnly = true)
    public List<PersonRole> getList(PersonRoleDTO dto) {
        final QueryWrapper<PersonRole> queryWrapper = createQueryWrapper(dto);
        final List<PersonRole> list = super.list(queryWrapper);
        queryWrapper.clear();
        return list;
    }

    /**
     * 根据条件查询列表数据
     *
     * @param dto 查询参数
     * @return 列表数据，转换成页面对象
     */
    @Transactional(readOnly = true)
    public List<PersonRoleDTO> getListDto(PersonRoleDTO dto) {
        return modelToDtoList(getList(dto));
    }

    /**
     * 分页数据
     *
     * @param page 分页对象
     * @param dto  查询参数
     * @return 分页信息
     */
    @Transactional(readOnly = true)
    public IPage<PersonRoleDTO> page(Page<PersonRole> page, PersonRoleDTO dto) {
        final QueryWrapper<PersonRole> queryWrapper = createQueryWrapper(dto, "pr.");
        final IPage<PersonRoleDTO> iPage = getBaseMapper().page(page, queryWrapper);
        queryWrapper.clear();
        return iPage;
    }

    /**
     * 根据唯一性索引获得数据
     * <p>
     * param personId
     * param roleId
     *
     * @return 单条数据
     */
    @Transactional(readOnly = true)
    public PersonRole getUniqueByPersonIdAndRoleId(String personId, String roleId) {
        final QueryWrapper<PersonRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(Constants.PERSON_ID, personId);
        queryWrapper.eq(Constants.ROLE_ID, roleId);
        final String key = RedisTemplateUtil.getRedisCacheKey(PREFIX_UNIQUE_PERSON_ID, personId, roleId);
        final PersonRole model = redisTemplateUtil.get(new CacheKey().setKey(key).setTimeout(ConstantDto.REDIS_TIME_OUT_DAY), () -> super.getOne(queryWrapper, false), PersonRole.class);
        queryWrapper.clear();
        return model;
    }

    /**
     * 验证唯一性索引
     *
     * @param dto 页面对象
     */
    private void checkUnique(PersonRoleDTO dto) {
        PersonRole model = getUniqueByPersonIdAndRoleId(dto.getPersonId(), dto.getRoleId());
        if (null != model) {
            checkUnique(dto, model);
        }
    }

    /**
     * 验证唯一性索引
     *
     * @param dto   页面对象
     * @param model 根据约束字段查询出来的数据库对象
     */
    private void checkUnique(PersonRoleDTO dto, PersonRole model) {
        if (!StringUtils.hasLength(dto.getPersonRoleId())) {
            throw new ApiException("数据已存在，请勿重复添加");
        }
        if (!dto.getPersonRoleId().equals(model.getPersonRoleId())) {
            throw new ApiException("数据已存在，请勿重复添加");
        }
    }

    /**
     * 清空缓存
     *
     * @param model 数据库对象
     */
    @Transactional(readOnly = true)
    public void clearCache(PersonRole model) {
        String personId = model.getPersonId();
        if (!StringUtils.hasLength(personId)) {
            final PersonRole personRole = getById(model.getPersonRoleId());
            if (personRole != null) {
                personId = personRole.getPersonId();
                personRole.freeData();
            }
        }
        String key = RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, model.getPersonRoleId());
        redisTemplateUtil.deleteByKey(key);
        key = RedisTemplateUtil.getRedisCacheKey(PREFIX_UNIQUE_PERSON_ID, model.getPersonId(), model.getRoleId());
        redisTemplateUtil.deleteByKey(key);
        if (StringUtils.hasLength(personId)) {
            key = RedisTemplateUtil.getRedisCacheKey(PREFIX_PERSON_ID, personId);
            redisTemplateUtil.deleteByKey(key);
        }
    }

    /**
     * 批量清空缓存
     *
     * @param list 数据库对象
     */
    public void clearCaches(List<PersonRole> list) {
        if (null == list || list.isEmpty()) {
            return;
        }
        list.forEach(this::clearCache);
    }

    /**
     * 根据角色ID查询用户数量
     *
     * @param roleId 角色ID
     * @return 属于这个角色的用户数量
     */
    public long getCount(String roleId) {
        final QueryWrapper<PersonRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(Constants.ROLE_ID, roleId);
        final long count = count(queryWrapper);
        queryWrapper.clear();
        return count;
    }

    /**
     * 根据人员ID获得角色列表
     *
     * @param personId 人员ID
     * @return 角色列表
     */
    public List<RoleDTO> getRoleByPersonId(String personId) {
        final QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("pr." + Constants.PERSON_ID, personId);
        queryWrapper.orderByAsc("r.role_sort");
        final String key = RedisTemplateUtil.getRedisCacheKey(PREFIX_PERSON_ID, personId);
        return roleRedisTemplateUtil.getList(new CacheKey().setKey(key), () -> super.getBaseMapper().getRoleByPersonId(queryWrapper), RoleDTO.class);
    }

    /**
     * 根据角色ID获得对应的人员列表
     *
     * @param roleId 角色ID
     * @return 角色人员列表
     */
    public List<PersonRole> getPersonRoleByRoleId(String roleId) {
        final QueryWrapper<PersonRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(Constants.ROLE_ID, roleId);
        final List<PersonRole> list = list(queryWrapper);
        queryWrapper.clear();
        return list;
    }

    /**
     * 根据人员ID获得角色ID列表
     *
     * @param personId 人员ID
     * @return 角色ID列表
     */
    public List<String> getRoleCodeByPersonId(String personId) {
        final List<RoleDTO> list = getRoleByPersonId(personId);
        if (list.isEmpty()) {
            return Collections.emptyList();
        }
        return list.stream().map(RoleDTO::getRoleCode).collect(toList());
    }

    /**
     * 数据库对象转页面对象
     *
     * @param model 数据库对象
     * @return 页面对象
     */
    public PersonRoleDTO modelToDto(PersonRole model) {
        if (null == model) {
            return null;
        }
        final PersonRoleDTO dto = new PersonRoleDTO();
        dto.setPersonRoleId(model.getPersonRoleId());
        dto.setAddTime(model.getAddTime());
        dto.setUpdateTime(model.getUpdateTime());
        dto.setPersonId(model.getPersonId());
        dto.setRoleId(model.getRoleId());
        buildDisplay(dto);
        return dto;
    }

    /**
     * 页面对象转数据库对象
     *
     * @param dto 页面对象
     * @return 数据库对象
     */
    public PersonRole dtoToModel(PersonRoleDTO dto) {
        if (null == dto) {
            return null;
        }
        final PersonRole model = new PersonRole();
        model.setPersonRoleId(dto.getPersonRoleId());
        model.setAddTime(dto.getAddTime());
        model.setUpdateTime(dto.getUpdateTime());
        model.setPersonId(dto.getPersonId());
        model.setRoleId(dto.getRoleId());
        return model;
    }

    /**
     * 码表转换
     *
     * @param dto 页面对象
     */
    public void buildDisplay(PersonRoleDTO dto) {
        dto.setPersonIdDisplay(personService.idToDisplay(dto.getPersonId()));
        dto.setRoleIdDisplay(roleService.idToDisplay(dto.getRoleId()));
    }

    /**
     * 数据库对象转页面对象
     *
     * @param list 数据库对象
     * @return 页面对象
     */
    public List<PersonRoleDTO> modelToDtoList(List<PersonRole> list) {
        if (null == list) {
            return Collections.emptyList();
        }
        return list.stream().map(this::modelToDto).collect(toList());
    }

    /**
     * 页面对象转数据库对象
     *
     * @param list 页面对象
     * @return 数据库对象
     */
    public List<PersonRole> dtoToModelList(List<PersonRoleDTO> list) {
        if (null == list) {
            return Collections.emptyList();
        }
        return list.stream().map(this::dtoToModel).collect(toList());
    }

    /**
     * 批量检查主键在数据库中是否存在
     *
     * @param ids 主键集合
     * @return 主键查询出来的集合
     */
    public List<PersonRole> checkIds(List<String> ids) {
        if (null == ids || ids.isEmpty()) {
            throw new ApiException("ids不能为空");
        }
        final List<PersonRole> list = super.listByIds(ids);
        if (list.isEmpty()) {
            throw new ApiException("ids不存在" + Arrays.toString(ids.toArray()));
        }
        if (ids.size() == list.size()) {
            return list;
        }
        List<String> dbIds = list.stream().map(PersonRole::getPersonRoleId).collect(toList());
        final List<String> notExistsList = ids.stream().filter(id -> !dbIds.contains(id)).collect(toList());
        dbIds.clear();
        if (!notExistsList.isEmpty()) {
            throw new ApiException("ids不存在" + Arrays.toString(notExistsList.toArray()));
        }
        return list;
    }

    /**
     * 获得当前用户的角色列表
     *
     * @param personId 人员ID
     * @return 角色列表
     */
    public List<String> getPersonRoleById(String personId) {
        final List<PersonRole> roleList = getListByPersonId(personId);
        if (roleList.isEmpty()) {
            return Collections.emptyList();
        }
        return roleList.stream().map(PersonRole::getPersonRoleId).collect(toList());
    }

    /**
     * 获得当前用户的角色列表
     *
     * @param personId 人员ID
     * @return 角色列表
     */
    private List<PersonRole> getListByPersonId(String personId) {
        final QueryWrapper<PersonRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(Constants.PERSON_ID, personId);
        final List<PersonRole> list = list(queryWrapper);
        queryWrapper.clear();
        return list;
    }

    /**
     * 获得当前用户的角色列表
     *
     * @param personId 人员ID
     * @return 角色列表
     */
    public List<PersonRoleDTO> getListByPersonIdDto(String personId) {
        final List<PersonRole> list = getListByPersonId(personId);
        if (null == list || list.isEmpty()) {
            return Collections.emptyList();
        }
        return list.stream().map(this::modelToDto).collect(toList());
    }

    /**
     * 根据人员ID查询角色列表
     *
     * @param personIds 多个人员
     * @return 角色列表
     */
    public List<PersonRoleDTO> getListByPersonIdsDto(List<String> personIds) {
        final QueryWrapper<PersonRole> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("pr." + Constants.PERSON_ID, personIds);
        final List<PersonRoleDTO> list = getBaseMapper().getRoleByPersonIds(queryWrapper);
        queryWrapper.clear();
        return list;
    }

    /**
     * 拼装查询参数，除了主键的其他字段会自动加入进来
     *
     * @param dto 查询参数，不为空的属性会被自动加入
     */
    public QueryWrapper<PersonRole> createQueryWrapper(PersonRoleDTO dto) {
        return createQueryWrapper(dto, "");
    }

    /**
     * 拼装查询参数，除了主键的其他字段会自动加入进来
     *
     * @param dto        查询参数，不为空的属性会被自动加入
     * @param tableAlias 表别名
     */
    public QueryWrapper<PersonRole> createQueryWrapper(PersonRoleDTO dto, String tableAlias) {
        final QueryWrapper<PersonRole> queryWrapper = new QueryWrapper<>();
        if (null != dto) {
            if (StringUtils.hasLength(tableAlias) && tableAlias.charAt(tableAlias.length() - 1) != '.') {
                tableAlias += ".";
            }
            if (StringUtils.hasLength(dto.getPersonId())) {
                queryWrapper.eq(tableAlias + Constants.PERSON_ID, dto.getPersonId());
            }
            if (StringUtils.hasLength(dto.getRoleId())) {
                queryWrapper.eq(tableAlias + Constants.ROLE_ID, dto.getRoleId());
            }
        }
        queryWrapper.orderByAsc(tableAlias + Constants.PERSON_ID);
        queryWrapper.orderByAsc(tableAlias + Constants.ROLE_ID);
        return queryWrapper;
    }

    @Transactional(rollbackFor = Exception.class)
    public void setRolePersonRelations(RolePersonRelationsDTO RolePersonRelationsDTO) {
        final String roleId = RolePersonRelationsDTO.getRoleId();
        final List<String> personList = RolePersonRelationsDTO.getPersonList();
        final List<String> existsPersonList = getExistsPerson(personList);
        final LocalDateTime now = LocalDateTime.now();
        final List<PersonRole> personRoleList = new ArrayList<>(existsPersonList.size());
        personList.forEach(personId -> {
            final PersonRole personRole = new PersonRole();
            personRole.setPersonId(personId);
            personRole.setRoleId(roleId);
            if (Boolean.TRUE.equals(isExists(personRole))) {
                //已经存在的不处理
                personRole.freeData();
            } else {
                personRole.setPersonRoleId(IdWorker.getIdStr());
                personRole.setAddTime(now);
                clearCache(personRole);
                personRoleList.add(personRole);
            }
        });
        if (!personRoleList.isEmpty()) {
            saveBatch(personRoleList);
            personRoleList.clear();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void deleteRolePersonRelations(RolePersonRelationsDTO RolePersonRelationsDTO) {
        final String roleId = RolePersonRelationsDTO.getRoleId();
        final List<String> personList = RolePersonRelationsDTO.getPersonList();
        final List<String> existsPersonList = getExistsPerson(personList);
        if (!existsPersonList.isEmpty()) {
            List<String> personRoleIdList = existsPersonList.stream().map(personId -> {
                PersonRole personRole = getUniqueByPersonIdAndRoleId(personId, roleId);
                if (null != personRole) {
                    return personRole.getPersonRoleId();
                }
                return null;
            }).filter(StringUtils::hasLength).collect(toList());
            if (!personRoleIdList.isEmpty()) {
                removeBatchIds(personRoleIdList);
                personRoleIdList.clear();
            }
        }
    }

    private List<String> getExistsPerson(List<String> personList) {
        final List<String> existsPersonList = personService.getBaseMapper().getIds(personList);
        if (existsPersonList.isEmpty()) {
            throw new ApiException("人员信息不存在" + Arrays.toString(personList.toArray()));
        }
        if (existsPersonList.size() != personList.size()) {
            throw new ApiException("人员信息不存在" + Arrays.toString(personList.stream().filter(personId -> !existsPersonList.contains(personId)).toArray()));
        }
        return existsPersonList;
    }

    public PersonService getPersonService() {
        return personService;
    }

    public RoleService getRoleService() {
        return roleService;
    }
}